/*
 * @Author: zzd993
 * @Date: 2023-02-09 15:16:04
 * @LastEditors: zzd993
 * @LastEditTime: 2023-02-17 15:55:43
 * @FilePath: \手写vue2源码\src\state.js
 * Copyright (c) 2023 by ZZD, All Rights Reserved. 
 */

import Dep from "./observe/dep"
import { observe } from "./observe/index"
import Watcher from "./observe/watcher"
export function initState(vm) {
    const opts = vm.$options  // 获取所有选项
    if(opts.data) {
        initData(vm)
    }
    if(opts.computed) {
        initComputed(vm)
    }
    if(opts.watch) {
        initWatch(vm)
    }
}


// watch的写法可能是字符串、函数、数组等，统一用$watch来处理
function initWatch(vm) {
    let watch = vm.$options.watch
    for(let key in watch) {
        const handler = watch[key]  // 字符串 数组 函数
        if(Array.isArray(handler)) {
            for(let i = 0; i < handler.length; i++) {
                createWatcher(vm, key, handler[i])
            }
        } else {
            createWatcher(vm, key, handler)
        }
    }
}

function createWatcher(vm, key, handler) {
    // 字符串 函数
    if(typeof handler === 'string') {
        handler = vm[handler]
    }
    return vm.$watch(key, handler)
}


function initData(vm) {
    let data = vm.$options.data // 可能是函数或对象
    data = typeof data === 'function' ? data.call(vm) : data

    vm._data = data
    // 对数据进行劫持
    observe(data)

    // 将vm._data用vm来代理就可以了
    for(let key in data) {
        proxy(vm, '_data', key)
    }
    
}

function proxy(vm, target, key) {
    // 在vm上定义属性，访问时代理到vm._data上
    Object.defineProperty(vm, key, {
        get() {
            return vm[target][key]
        },
        set(newVal) {
            vm[target][key] = newVal
        }
    })
}


function initComputed(vm) {
    const computed = vm.$options.computed
    const watcher = vm._computedWatchers
    for(let key in computed) {
        let userDef = computed[key]

        // 需要监控计算属性中get的变化
        let fn = typeof userDef === 'function' ? userDef : userDef.get
        // 如果直接new watcher默认就会执行fn，将属性和watcher对应起来
        watcher[key] = new Watcher(vm, fn, { lazy: true })

        
        defineComputed(vm, key, userDef)
    }
}

function defineComputed(target, key, userDef) {
        // const getter = typeof userDef === 'function' ? userDef : userDef.get
        const setter = userDef.set || (() => {})
        Object.defineProperty(target, key, {
            get: createComputedGetter(key),
            set: setter
        })
}

function createComputedGetter(key) {
    // 我们需要检测是否执行这个getter
    return function() { 
        const watcher = this._computedWatchers[key]   // 获取到对应属性的watcher
        if(watcher.dirty) {
            // 如果是脏的(第一次)就去执行用户传入的函数
            watcher.evaluate()
        }
        if(Dep.target) {
            watcher.depend()
        }
        return watcher.value
    }
}